commonlibsse_ng\re\c/
ControlMap.rs

1use crate::re::BSFixedString::BSFixedString;
2use crate::re::BSInputDeviceManager::BSInputDeviceManager;
3use crate::re::BSTArray::BSTArray;
4use crate::re::BSTEvent::BSTEventSource;
5use crate::re::InputDevices::{INPUT_DEVICE, INPUT_DEVICE_VR_CEnum};
6use crate::re::PCGamepadType::PC_GAMEPAD_TYPE_CEnum;
7use crate::re::UserEventEnabled::UserEventEnabled;
8use crate::re::UserEvents::{INPUT_CONTEXT_ID, INPUT_CONTEXT_ID_VR_CEnum, USER_EVENT_FLAG};
9use crate::rel::module::ModuleState;
10use crate::rel::relocation::{RelocationError, raw_pointer_as_mut, raw_pointer_as_ref};
11use crate::skse::version::RUNTIME_SSE_1_6_1130;
12
13#[repr(C)]
14#[derive(Debug)]
15pub struct ControlMap {
16    pub __base: [u8; 1], // 0x000: BSTSingletonSDM<ControlMap> address
17    pub __base1: BSTEventSource<UserEventEnabled>, // 0x008: vtable size
18    pub controlMap: [*mut InputContext; INPUT_CONTEXT_ID_VR_CEnum::count()], // 0x060
19}
20const _: () = assert!(core::mem::size_of::<ControlMap>() == 0xE8);
21
22impl ControlMap {
23    /// Gets the singleton instance of `ControlMap`.
24    #[commonlibsse_ng_derive_internal::relocate(
25        cast_as = "*mut *mut ControlMap",
26        default = "None",
27        deref_once,
28        id(se = 514705, ae = 400863)
29    )]
30    pub fn get_singleton() -> Option<&'static ControlMap> {
31        |deref_type: DerefType| unsafe { deref_type.as_ref() }
32    }
33
34    /// Gets the mutable singleton instance of `ControlMap`.
35    #[commonlibsse_ng_derive_internal::relocate(
36        cast_as = "*mut *mut ControlMap",
37        default = "None",
38        deref_once,
39        id(se = 514705, ae = 400863)
40    )]
41    pub fn get_singleton_mut() -> Option<&'static mut ControlMap> {
42        |deref_type: DerefType| unsafe { deref_type.as_mut() }
43    }
44
45    /// Gets fields whose offset is determined at runtime.
46    ///
47    /// # Errors
48    /// This function may return an error if the module's runtime is not available or if any error occurs while fetching the runtime state.
49    /// Specifically, it calls `ModuleState::map_active`, which could result in an error.
50    pub fn get_runtime_data(&self) -> Result<&RUNTIME_DATA, RelocationError> {
51        let (is_vr, is_ae_1_6_1130) = ModuleState::map_or_init(|module| {
52            (module.runtime.is_vr(), module.version >= RUNTIME_SSE_1_6_1130)
53        })?;
54
55        let this = self as *const Self;
56        let member_ptr = if is_ae_1_6_1130 {
57            this.wrapping_offset(0xf0).cast()
58        } else {
59            let offset = if is_vr { 0xE8 } else { 0x108 };
60            this.wrapping_offset(offset).cast()
61        };
62
63        Ok(unsafe { raw_pointer_as_ref(member_ptr) }?)
64    }
65
66    /// Gets mutable fields whose offset is determined at runtime.
67    ///
68    /// # Errors
69    /// This function may return an error if the module's runtime is not available or if any error occurs while fetching the runtime state.
70    /// Specifically, it calls `ModuleState::map_active_mut`, which could result in an error.
71    pub fn get_runtime_data_mut(&mut self) -> Result<&mut RUNTIME_DATA, RelocationError> {
72        let (is_vr, is_ae_1_6_1130) = ModuleState::map_or_init(|module| {
73            (module.runtime.is_vr(), module.version >= RUNTIME_SSE_1_6_1130)
74        })?;
75
76        let this = self as *mut Self;
77
78        let member_ptr = if is_ae_1_6_1130 {
79            this.wrapping_offset(0xf0).cast()
80        } else {
81            let offset = if is_vr { 0xE8 } else { 0x108 };
82            this.wrapping_offset(offset).cast()
83        };
84
85        Ok(unsafe { raw_pointer_as_mut(member_ptr) }?)
86    }
87
88    pub fn allow_text_input(&mut self, allow: bool) -> Option<i8> {
89        let text_entry_count = &mut (self.get_runtime_data_mut().ok()?.textEntryCount);
90
91        if allow {
92            if *text_entry_count != -1 {
93                *text_entry_count += 1;
94            }
95        } else if *text_entry_count != 0 {
96            *text_entry_count -= 1;
97        }
98
99        Some(*text_entry_count)
100    }
101
102    pub fn get_button_name_from_user_event(
103        &self,
104        event_id: &BSFixedString,
105        device: INPUT_DEVICE,
106    ) -> Option<BSFixedString> {
107        for input_context in self.controlMap {
108            let Some(input_context) = (unsafe { input_context.as_ref() }) else {
109                continue;
110            };
111
112            let device_mappings = input_context.deviceMappings.get(device.0 as usize)?;
113
114            for mapping in device_mappings {
115                if mapping.eventID != *event_id {
116                    continue;
117                }
118
119                let input_key = mapping.inputKey;
120                if input_key == 0xFF {
121                    break;
122                }
123
124                let input_device_manager = BSInputDeviceManager::get_singleton_mut()?;
125                if let Some(output) =
126                    input_device_manager.get_button_name_from_id(device, input_key as u32)
127                {
128                    return Some(output);
129                };
130            }
131        }
132
133        None
134    }
135
136    pub fn get_user_event_name(
137        &self,
138        button_id: u32,
139        device: INPUT_DEVICE,
140        context: INPUT_CONTEXT_ID,
141    ) -> Option<&BSFixedString> {
142        let input_ctx = self.controlMap.get(context.0 as usize)?;
143        let mappings = unsafe { input_ctx.as_ref() }?.deviceMappings.get(device.0 as usize)?;
144
145        // Instead of C++ `equal_range`
146        let search_target_key = button_id as u16;
147        let slice = mappings.as_slice();
148        let start = slice.partition_point(|x| x.inputKey < search_target_key);
149        let end = slice.partition_point(|x| x.inputKey <= search_target_key);
150
151        if end - start == 1 { Some(&slice[start].eventID) } else { None }
152    }
153
154    #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 67244, ae_id = 68544)]
155    #[inline]
156    pub fn pop_input_context(&mut self, context: INPUT_CONTEXT_ID) -> bool {}
157
158    #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 67243, ae_id = 68543)]
159    #[inline]
160    pub fn push_input_context(&mut self, context: INPUT_CONTEXT_ID) -> bool {}
161}
162
163#[repr(C)]
164#[derive(Debug, Clone)]
165pub struct RUNTIME_DATA {
166    pub linkedMappings: BSTArray<LinkedMapping>, // 0x0E8, VR: 0x108
167    pub contextPriorityStack: BSTArray<INPUT_CONTEXT_ID>, // 0x100, VR: 0x120
168    pub enabledControls: USER_EVENT_FLAG,        // 0x118, VR: 0x138
169    pub unk11C: USER_EVENT_FLAG,                 // 0x11C, VR: 0x13C
170    pub textEntryCount: i8,                      // 0x120, VR: 0x140
171    pub ignoreKeyboardMouse: bool,               // 0x121, VR: 0x141
172    pub ignoreActivateDisabledEvents: bool,      // 0x122, VR: 0x142
173    pub pad123: u8,                              // 0x123, VR: 0x143
174    pub gamePadMapType: PC_GAMEPAD_TYPE_CEnum,   // 0x124, VR: 0x144
175}
176
177#[repr(C)]
178#[derive(Debug, Clone)]
179pub struct UserEventMapping {
180    pub eventID: BSFixedString,              // 0x00
181    pub inputKey: u16,                       // 0x08
182    pub modifier: u16,                       // 0x08
183    pub indexInContext: i8,                  // 0x0C
184    pub remappable: bool,                    // 0x0D
185    pub linked: bool,                        // 0x0E
186    pub userEventGroupFlag: USER_EVENT_FLAG, // 0x10
187    pub pad14: u32,                          // 0x14
188}
189const _: () = assert!(core::mem::size_of::<UserEventMapping>() == 0x18);
190
191#[repr(C)]
192#[derive(Debug, Clone)]
193pub struct InputContext {
194    pub deviceMappings: [BSTArray<UserEventMapping>; INPUT_DEVICE_VR_CEnum::count()], // 0x10
195}
196const _: () = assert!(core::mem::size_of::<InputContext>() == 0xF0);
197
198#[repr(C)]
199#[derive(Debug, Clone, PartialEq)]
200pub struct LinkedMapping {
201    pub linkedMappingName: BSFixedString,       // 0x00
202    pub linkedMappingContext: INPUT_CONTEXT_ID, // 0x08
203    pub device: INPUT_DEVICE,                   // 0x0C
204    pub linkFromContext: INPUT_CONTEXT_ID,      // 0x10
205    pub pad14: u32,                             // 0x14
206    pub linkFromName: BSFixedString,            // 0x18
207}
208const _: () = assert!(core::mem::size_of::<LinkedMapping>() == 0x20);